package czy.demo.config;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

/**
 * RabbitMQ是高级消息队列协议(AMQP)的实现之一,有以下几个概念:
 * connection:连接,即连接工厂启动后会在rabbit server产生一个连接
 * channel:通道,一个连接肯定会产生一个通道,应该是长连接
 * exchange:消息队列的路由方式,有多种类型:direct fanout headers topic
 * queue:接收消息的队列
 */
@EnableRabbit
@Configuration
public class SpringRabbitMQConfig implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(SpringRabbitMQConfig.class);

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 可以在程序启动时使用Channel来创建Exchange Queue Binding这三者
     * 貌似没有更高级的方法了
     */
    @Override
    public void afterPropertiesSet() throws Exception {

        /**
         *  设置服务端确认应答回调,这个回调方法是与RabbitTemplate对应的,可以只设置一次
         *  如果全局只有一个RabbitTemplate,那么需要根据不同的CorrelationData对错误进行处理
         *  但是问题也很大,一个全局的ConfirmCallback很难对每个不同错误做出合适的处理
         *  如果每次发送消息时对ConfirmCallback进行临时设置,又会存在多线程并发问题
         */
        this.rabbitTemplate.setConfirmCallback((CorrelationData correlationData, boolean ack, String cause)->{
            logger.info("接收到服务端应答: "+ack);
            logger.info("关联数据: "+correlationData.getId());
            if(!ack){
                logger.info("错误为: "+cause);
            }
        });


        /**
         * 设置服务端返回消息回调,目前还不知道什么情况下,服务端会返回消息
         */
        this.rabbitTemplate.setReturnCallback(
                (Message message, int replyCode, String replyText,String exchange, String routingKey)-> {
                    logger.info("接收到服务端返回消息: ");
                    logger.info("replay code；"+replyCode);
                    logger.info("replay text: "+replyText);
                    logger.info("exchange；"+exchange);
                    logger.info("routingKey: "+routingKey);
                    logger.info("properties: "+message.getMessageProperties());
                    logger.info("body: "+new String(message.getBody()));
                }
                );


        /* 使用Channel在程序启动时创建交换机 队列 绑定等 */
        this.rabbitTemplate.execute(channel->{

            /* 创建的队列会绑定到默认交换器,以空字符串指代,并以queue名称为键 */
            channel.queueDeclare("default",true,false,true,null);

            /* 创建direct交换器,direct交换器根据routingKey匹配队列 */
            channel.exchangeDeclare("direct","direct",true);
            channel.queueDeclare("apple",true,false,true,null);
            channel.queueBind("apple","direct","a",null);
            channel.queueDeclare("banana",true,false,true,null);
            channel.queueBind("banana","direct","b",null);
            channel.queueDeclare("orange",true,false,true,null);
            channel.queueBind("orange","direct","o",null);


            return null;
        });
    }
}
